//
//  KooCalendarModel.m
//  KooIEDUCalendarDemo
//
//  Created by nowcoder on 2024.
//

#import "KooCalendarModel.h"

@implementation KooCalendarCellModel

#pragma mark - Initialization

- (instancetype)initWithDate:(NSDate *)date currentMonthDate:(NSDate *)currentMonthDate {
    self = [super init];
    if (self) {
        self.date = date;
        self.day = [self dayFromDate:date];
        [self updateStateBasedOnDate:date currentMonthDate:currentMonthDate];
    }
    return self;
}

#pragma mark - Private Methods

- (void)updateStateBasedOnDate:(NSDate *)date currentMonthDate:(NSDate *)currentMonthDate {
    NSCalendar *calendar = [NSCalendar currentCalendar];
    NSDate *today = [NSDate date];
    
    // 判断是否是今日
    if ([calendar isDate:date inSameDayAsDate:today]) {
        self.state = KooCalendarCellStateToday;
    }
    // 判断是否属于当前月份
    else if (![calendar isDate:date equalToDate:currentMonthDate toUnitGranularity:NSCalendarUnitMonth]) {
        self.state = KooCalendarCellStateOtherMonth;
    }
    // 判断是否是过去的日期（禁用）
    else if ([date compare:today] == NSOrderedAscending && 
             ![calendar isDate:date inSameDayAsDate:today]) {
        self.state = KooCalendarCellStateDisabled;
    }
    else {
        self.state = KooCalendarCellStateNormal;
    }
}

- (NSInteger)dayFromDate:(NSDate *)date {
    NSCalendar *calendar = [NSCalendar currentCalendar];
    NSDateComponents *components = [calendar components:NSCalendarUnitDay fromDate:date];
    return components.day;
}

#pragma mark - Public Methods

- (void)updateState:(KooCalendarCellState)state {
    self.state = state;
}

#pragma mark - Getters

- (BOOL)isToday {
    return self.state == KooCalendarCellStateToday;
}

- (BOOL)isSelected {
    return self.state == KooCalendarCellStateSelected;
}

- (BOOL)isSelectable {
    return self.state != KooCalendarCellStateDisabled && 
           self.state != KooCalendarCellStateOtherMonth &&
           self.state != KooCalendarCellStateToday;
}

- (BOOL)isCurrentMonth {
    return self.state != KooCalendarCellStateOtherMonth;
}

@end

@interface KooCalendarModel ()

@property (nonatomic, strong) NSMutableArray<KooCalendarCellModel *> *mutableCellModels;
@property (nonatomic, strong) NSMutableSet<NSDate *> *mutableSelectedDates;
@property (nonatomic, strong) NSDate *today;
@property (nonatomic, strong) NSCalendar *calendar;
@property (nonatomic, strong) NSDateFormatter *dateFormatter;

@end

@implementation KooCalendarModel

#pragma mark - Initialization

- (instancetype)initWithCurrentDate:(NSDate *)currentDate {
    self = [super init];
    if (self) {
        self.currentMonthDate = currentDate ?: [NSDate date];
        [self setupCalendarComponents];
        [self generateCellModelsForCurrentMonth];
    }
    return self;
}

- (void)setupCalendarComponents {
    self.calendar = [NSCalendar currentCalendar];
    self.calendar.firstWeekday = 1; // 星期日为第一天
    
    self.dateFormatter = [[NSDateFormatter alloc] init];
    self.dateFormatter.calendar = self.calendar;
    
    self.today = [NSDate date];
    self.mutableSelectedDates = [[NSMutableSet alloc] init];
    self.mutableCellModels = [[NSMutableArray alloc] init];
}

#pragma mark - Data Generation

- (void)generateCellModelsForCurrentMonth {
    [self.mutableCellModels removeAllObjects];
    
    NSDate *firstDayOfMonth = [self firstDayOfCurrentMonth];
    NSDate *lastDayOfMonth = [self lastDayOfCurrentMonth];
    
    // 获取第一天是星期几
    NSInteger firstWeekday = [self.calendar component:NSCalendarUnitWeekday fromDate:firstDayOfMonth];
    
    // 计算需要显示的第一个日期（可能是上个月的日期）
    NSInteger daysBeforeFirstDay = (firstWeekday - self.calendar.firstWeekday + 7) % 7;
    NSDate *startDate = [self.calendar dateByAddingUnit:NSCalendarUnitDay 
                                                  value:-daysBeforeFirstDay 
                                                 toDate:firstDayOfMonth 
                                                options:0];
    
    // 计算需要的行数：确保包含当前月的最后一天，但不多显示额外的行
    NSInteger totalDaysNeeded = [self calculateRequiredDaysCount:startDate lastDayOfMonth:lastDayOfMonth];
    
    // 生成实际需要的天数数据
    NSDate *currentDate = startDate;
    for (NSInteger i = 0; i < totalDaysNeeded; i++) {
        KooCalendarCellModel *cellModel = [[KooCalendarCellModel alloc] 
                                          initWithDate:currentDate 
                                          currentMonthDate:self.currentMonthDate];
        
        // 如果日期已被选中，更新状态
        if ([self.mutableSelectedDates containsObject:currentDate]) {
            cellModel.state = KooCalendarCellStateSelected;
        }
        
        [self.mutableCellModels addObject:cellModel];
        
        currentDate = [self.calendar dateByAddingUnit:NSCalendarUnitDay 
                                                value:1 
                                               toDate:currentDate 
                                              options:0];
    }
}

- (NSInteger)calculateRequiredDaysCount:(NSDate *)startDate lastDayOfMonth:(NSDate *)lastDayOfMonth {
    // 计算从开始日期到月末最后一天的天数
    NSTimeInterval timeInterval = [lastDayOfMonth timeIntervalSinceDate:startDate];
    NSInteger daysBetween = (NSInteger)(timeInterval / (24 * 60 * 60)) + 1; // +1 包含最后一天
    
    // 确保天数是7的倍数（完整的周）
    NSInteger weeks = (daysBetween + 6) / 7; // 向上取整
    
    return weeks * 7;
}

#pragma mark - Public Methods

- (void)switchToMonth:(NSDate *)date {
    self.currentMonthDate = date;
    [self generateCellModelsForCurrentMonth];
}

- (BOOL)selectDate:(NSDate *)date {
    // 检查日期是否可选择
    if (![self isDateSelectable:date onlyFutureDates:YES]) {
        return NO;
    }
    
    // 添加到选中集合
    [self.mutableSelectedDates addObject:date];
    
    // 更新对应的cellModel状态
    [self updateCellModelStateForDate:date state:KooCalendarCellStateSelected];
    
    return YES;
}

- (void)deselectDate:(NSDate *)date {
    [self.mutableSelectedDates removeObject:date];
    
    // 恢复cellModel的原始状态
    KooCalendarCellModel *cellModel = [self cellModelForDate:date];
    if (cellModel) {
        [cellModel updateStateBasedOnDate:date currentMonthDate:self.currentMonthDate];
    }
}

- (void)clearSelectedDates {
    NSArray *selectedDates = [self.mutableSelectedDates allObjects];
    [self.mutableSelectedDates removeAllObjects];
    
    // 恢复所有选中日期的原始状态
    for (NSDate *date in selectedDates) {
        KooCalendarCellModel *cellModel = [self cellModelForDate:date];
        if (cellModel) {
            [cellModel updateStateBasedOnDate:date currentMonthDate:self.currentMonthDate];
        }
    }
}

- (void)setPreSelectedDates:(NSArray<NSDate *> *)dates {
    [self.mutableSelectedDates removeAllObjects];
    
    for (NSDate *date in dates) {
        if ([self isDateSelectable:date onlyFutureDates:YES]) {
            [self.mutableSelectedDates addObject:date];
        }
    }
    
    // 重新生成数据以更新状态
    [self generateCellModelsForCurrentMonth];
}

#pragma mark - Query Methods

- (BOOL)isDateSelected:(NSDate *)date {
    return [self.mutableSelectedDates containsObject:date];
}

- (BOOL)isToday:(NSDate *)date {
    return [self.calendar isDate:date inSameDayAsDate:self.today];
}

- (BOOL)isDateSelectable:(NSDate *)date onlyFutureDates:(BOOL)onlyFutureDates {
    if (onlyFutureDates) {
        // 只能选择明天之后的日期（今天也不可选择）
        return [date compare:self.today] == NSOrderedDescending;
    }
    return YES;
}

- (BOOL)isDateInCurrentMonth:(NSDate *)date {
    return [self.calendar isDate:date equalToDate:self.currentMonthDate toUnitGranularity:NSCalendarUnitMonth];
}

- (NSDate *)firstDayOfCurrentMonth {
    NSDateComponents *components = [self.calendar components:NSCalendarUnitYear | NSCalendarUnitMonth 
                                                    fromDate:self.currentMonthDate];
    components.day = 1;
    return [self.calendar dateFromComponents:components];
}

- (NSDate *)lastDayOfCurrentMonth {
    NSDate *firstDay = [self firstDayOfCurrentMonth];
    NSDate *firstDayOfNextMonth = [self.calendar dateByAddingUnit:NSCalendarUnitMonth 
                                                            value:1 
                                                           toDate:firstDay 
                                                          options:0];
    return [self.calendar dateByAddingUnit:NSCalendarUnitDay 
                                     value:-1 
                                    toDate:firstDayOfNextMonth 
                                   options:0];
}

- (NSDate *)nextMonthFromDate:(NSDate *)date {
    return [self.calendar dateByAddingUnit:NSCalendarUnitMonth 
                                     value:1 
                                    toDate:date 
                                   options:0];
}

- (NSDate *)previousMonthFromDate:(NSDate *)date {
    return [self.calendar dateByAddingUnit:NSCalendarUnitMonth 
                                     value:-1 
                                    toDate:date 
                                   options:0];
}

#pragma mark - Format Methods

- (NSString *)formatDate:(NSDate *)date withFormat:(NSString *)format {
    self.dateFormatter.dateFormat = format;
    return [self.dateFormatter stringFromDate:date];
}

- (NSInteger)dayFromDate:(NSDate *)date {
    NSDateComponents *components = [self.calendar components:NSCalendarUnitDay fromDate:date];
    return components.day;
}

- (BOOL)isSameDay:(NSDate *)date1 withDate:(NSDate *)date2 {
    return [self.calendar isDate:date1 inSameDayAsDate:date2];
}

#pragma mark - Private Helper Methods

- (void)updateCellModelStateForDate:(NSDate *)date state:(KooCalendarCellState)state {
    KooCalendarCellModel *cellModel = [self cellModelForDate:date];
    if (cellModel) {
        [cellModel updateState:state];
    }
}

- (KooCalendarCellModel *)cellModelForDate:(NSDate *)date {
    for (KooCalendarCellModel *cellModel in self.mutableCellModels) {
        if ([self isSameDay:cellModel.date withDate:date]) {
            return cellModel;
        }
    }
    return nil;
}

#pragma mark - Getters

- (NSArray<KooCalendarCellModel *> *)cellModels {
    return [self.mutableCellModels copy];
}

- (NSMutableSet<NSDate *> *)selectedDates {
    return self.mutableSelectedDates;
}

@end 